<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>“菜单” （menubar）和“工具栏”（toolbars）</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, menus, toolbars, GTK+ library, C">
<meta nam="description" content="Menus and Toolbars in GTK+">
<meta name="language" content="zh, en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<div class="content">

<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>“菜单” （menubar）和“工具栏”（toolbars）</h1>


<p>
在这个部分的GTK+程序设计教程中，我们使用“菜单”和“工具栏”。
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>

<p>
<b>“菜单”（ menubar）</b> 是GUI程序中最为常见的部分之一。各种各样的命令和功能都可以借以“菜单”来实现。 当我们习惯在终端（console）中启动应用程序的时候，必须要记得很多复杂的命令和参数 ，在本章节中我们将 这一切都转化为可见的操作。菜单和工具栏中标准化的操作，将让你摆脱学习新软件所耗费的大量时间和精力。 
</p>


<h2>简单的菜单示列</h2>

<p>
在我们的第一个例子中，我们将生成一个含有文件菜单的菜单栏。文件菜单将只有一个菜单条（menu item）。如果点击这个菜单条程序将退出。
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *quit;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  file = gtk_menu_item_new_with_label("File");
  quit = gtk_menu_item_new_with_label("Quit");

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
生成一个菜单栏的确会让人有点疑惑。我们要牢记的是一个菜单栏和一个菜单都是源属于同一个构件的，也就是菜单外壳<b>（menu shell）</b>。菜单选项<b>（menu items ）</b>是一个只对菜单有效的子构件。他们通常用来实现子菜单。
</p>

<pre class="explanation">
 menubar = gtk_menu_bar_new();
 filemenu = gtk_menu_new();
</pre>

<p>
在上面的代码中我们生成了一个菜单栏构件（menubar）和一个菜单构件(menu)。
</p>

<pre class="explanation">
 gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
</pre>

<p>
上面的代码就会生成一个名为“文件”的菜单。这也就是说其实菜单栏就是一个菜单外壳。很显然这里的文件菜单也是一个菜单外壳。这就是为什么我们把文件菜单称为子菜单或者说是一个子外壳。

</p>

<pre class="explanation">
 gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
 gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
</pre>

<p>
菜单选项由函数  <b class="keyword">gtk_menu_shell_append()</b> f来实现。然后一般情况下菜单选项被填加进菜单外壳里。 在我们的这个例子中，“quit”菜单选项是被填加进“file”菜单栏里，然后类似的是“file”菜单选项被填加进菜单中（menubar）。

</p>

<pre class="explanation">
  g_signal_connect(G_OBJECT(quit), "activate",
        G_CALLBACK(gtk_main_quit), NULL);
</pre>

<p>
 当你单击“quit”菜单按钮，程序就会退出。 


</p>

<br>
<img src="../images/simplemenu.png" alt="Simple menu">
<div class="figure">Figure: Simple menu</div>

<h2>图象菜单, mnemonics &amp;accelerators</h2>

<p>
在接下来的这个例子中，我们将更进一步的去探索，在GTK+系统中我们可以应用的功能。<b>Accelerators</b> 是快捷键的意思，用来方便的用键盘上的组合键激活一个菜单选项。<b> Mnemonics</b>也是是快捷键用于用于GUI的基础。他们的具体表现都为带有下画线的字符。（译者：似乎说的不清楚啊，呵呵。具体的区别还是请参见下面的具体代码实现以及对应的程序效果图）
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;
#include &lt;gdk/gdkkeysyms.h&gt;


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *new;
  GtkWidget *open;
  GtkWidget *quit;

  GtkWidget *sep;

  GtkAccelGroup *accel_group = NULL;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);

  file = gtk_menu_item_new_with_mnemonic("_File");
  new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
  open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
  sep = gtk_separator_menu_item_new();
  quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

  gtk_widget_add_accelerator(quit, "activate", accel_group, 
      GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 


  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), new);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
在上面的整个代码程序中，向大家展示了是如何向一个菜单选项中去填加一个图象的。当然也包括了如何使用accelerator 以及 mnemonics 。 


</p>

<pre class="explanation">
  accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
  ...
  quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
  gtk_widget_add_accelerator(quit, "activate", accel_group, 
      GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 
</pre>

<p>
一个 accelerator 组有好多个accelerators快捷键。这里我们生成了一个“Ctrl + q” accelerators快捷键。
</p>

<pre class="explanation">
 file = gtk_menu_item_new_with_mnemonic("_File");
</pre>

<p>
我们需要调用函数 <b class="keyword">gtk_menu_item_new_with_mnemonic()</b> 来生成一个mnemonic快捷键。你可以按下键盘上的“ Alt + F”就可以看到. mnemonic快捷键的效果。

</p>

<pre class="explanation">
 new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
 open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
</pre>

<p>
在上面的代码中我们生成了两个带有图象的菜单选项。在所调用的函数的第二个参数中，我们设置为NULL，这样达到的效果是我们自动生成了accelerators快捷键。我们也为菜单选项分别提供了图象与文字。 
</p>

<pre class="explanation">
 sep = gtk_separator_menu_item_new();
</pre>

<p>
菜单选项能够被一个水平的分割线隔开。这样的话我们就可以从逻辑上把一些菜单选项给区分开来。
</p>


<br>
<img src="../images/menu.png" alt="Menu example">
<div class="figure">Figure: Menu example</div>

<h2>选择（Check）菜单选项 （menu item）</h2>

<p>
<b class="keyword">GtkCheckMenuItem</b>  便是一个可以生成带有选择的菜单选项。
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;


void toggle_statusbar(GtkWidget *widget, gpointer statusbar) 
{
  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
    gtk_widget_show(statusbar);
  } else {
    gtk_widget_hide(statusbar);
  }
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *viewmenu;
  GtkWidget *view;
  GtkWidget *tog_stat;
  GtkWidget *statusbar;
  

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "view statusbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  viewmenu = gtk_menu_new();

  view = gtk_menu_item_new_with_label("View");
  tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");
  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(tog_stat), TRUE);

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(viewmenu), tog_stat);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  statusbar = gtk_statusbar_new();
  gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(tog_stat), "activate", 
        G_CALLBACK(toggle_statusbar), statusbar);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
在我们的代码示例中，我们展示了如何去制造一个带有选择框的菜单选项。具体的功能是：如果选择框被选中则“状态栏”就会显示出来，反之则不会显示。 
</p>

<pre class="explanation">
 tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");
</pre>

<p>
函数<b class="keyword">gtk_check_menu_item_new_with_label()</b> 可以生成一个新的带有选择框的菜单选项。
</p>

<pre class="explanation">
 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
   gtk_widget_show(statusbar);
 } else {
   gtk_widget_hide(statusbar);
 }
</pre>

<p>
如果选择框被选中则“状态栏”就会显示出来，反之则不会显示。 
</p>

<br>
<img src="../images/checkmenuitem.png" alt="Check menu item">
<div class="figure">Figure: Check menu item</div>


<h2>工具栏（A toolbar）</h2>

<p>
菜单栏为我们编程时实现某种功能提供了方便与快捷。在接下来的章节中，我们将为你展示一种在特定情况下可以更加便捷的的方法——制造一个“工具栏。” 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;
  
  GtkWidget *toolbar;
  GtkToolItem *new;
  GtkToolItem *open;
  GtkToolItem *save;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "toolbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

  open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), open, -1);

  save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), save, -1);

  sep = gtk_separator_tool_item_new();
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
以上的代码中，我们制作了一个简单的工具栏实现。 


</p>

<pre class="explanation">
  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)
</pre>

<p>
从上面的两行代码中你应该可以看出来，我们生成了一个“崭新”的工具栏：）。我们还特地使他们都用图片来显示，没有包含文字。
</p>

<pre class="explanation">
 new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);
</pre>

<p>
从 stock中我们生成了一个新的工具栏按钮。要想把工具栏按钮插入到工具栏中，很简单！只需要调用函数 <b class="keyword">gtk_toolbar_insert()</b> 就可以搞定。 

</p>

<pre class="explanation">
 sep = gtk_separator_tool_item_new();
 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 
</pre>

<p>
上面的代码，我们生成了一个分割线把工具栏按钮们分开（只是逻辑上的分组需要）。 
</p>

<br>
<img src="../images/toolbar.png" alt="Toolbar">
<div class="figure">Figure: Toolbar</div>

<h2>功能失效（Undo redo）</h2>

<p>
在接下来的例子中，我们将展示一个神奇的功能：使工具栏中的一个按钮功能失效（让他变成阴影）。这在GUI设计中是一个常见的技巧。 举个例子：当我们把一片文章单击保存后，那个保存按钮就会变成阴影状，也就是功能失效了。就是来提示你：保存功能已经执行过了，不需要再执行保存功能了。 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;
#include &lt;string.h&gt;


void undo_redo(GtkWidget *widget,  gpointer item) 
{
  static int count = 2;
  const char *name = gtk_widget_get_name(widget);

  if ( strcmp(name, "undo") ) {
    count++;
  } else {
    count--;
  }
 
  if (count < 0) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  } 

  if (count > 5) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  }
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *toolbar;
  GtkToolItem *undo;
  GtkToolItem *redo;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "undoredo");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  undo = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
  gtk_widget_set_name(GTK_WIDGET(undo), "undo");
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);

  redo = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), redo, -1);

  sep = gtk_separator_tool_item_new();
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);


  g_signal_connect(G_OBJECT(undo), "clicked", 
        G_CALLBACK(undo_redo), redo);

  g_signal_connect(G_OBJECT(redo), "clicked", 
        G_CALLBACK(undo_redo), undo);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
我们的例子中用到了 GTK+ stock 来实现是 “功能有效”还是“功能失效”。当你单击几下 按钮后，那个按钮就会变成阴影状，也就是说他从“功能有效”变成了“功能失效”。 

</p>

<pre class="explanation">
 if (count &lt; 0) {
    gtk_widget_set_sensitive(widget, FALSE);
    gtk_widget_set_sensitive(item, TRUE);
 } 

 if (count &gt; 5) {
    gtk_widget_set_sensitive(widget, FALSE);
    gtk_widget_set_sensitive(item, TRUE);
 }
</pre>

<p>
 <b class="keyword">gtk_widget_set_sensitive()</b>  是被用来告诉计算机是否要击活一个工具栏按钮。 
</p>

<br>
<img src="../images/undoredo.png" alt="Undo redo">
<div class="figure">Figure: Undo redo</div>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified March 16, 2008  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
<span class="copyright">Translation &copy; 2008 chinalmy</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

